Сессия 1

1.1 Выделение итогового набора полей для одной записи

Для определения итогового набора полей необходимо проанализровать данные каждого файла. Распакованные данные находятся в папке data. Посмотрим на данные одной жалобы

Составим набор всех полей, созданных для данных записей. Приведенный ниже код сделает такой анализ для всех файлов. Вывод будет в следующем формате:

...

Структура вывода выше: уровень1-уровень2-уровень3-...-уровеньn

Проведя анализ данных каждого файла, я понял, что они все имеют почти что одинаковую структуру, различаясь разве что количеством элементов внутри полей feed, sidebar, reason.

Поле feed -- процесс рассмотрения заявки (ответы от ведомств, ответы пользователей, и др.) -- содержит массив стандартизированных словарей, каждый из которыз отражает сообщение той или иной стороны (или результаты модерации сообщения).

Поле sidebar содержит словарь данных об авторе, месте, к которому относится заявка, о здании, в отношении которого составлена жалоба.

Поле reason содержит словарь данных о категории жалоб. В нем есть категория жалобы, тип объекта, по отношению к которому делается жалоба, а также блок типов жалоб, к которым относится конкретно эта жалоба.

Очевидно, что из поля feed нам потребуется только самая первая в хронологическом порядке запись, так как в ней непосредственно отражен текст жалобы. Нам не будут нужны файлы и фотографии, а также файлы, прикрепленные к заявлению. В каждой такой жалобе будет нужно будет использовать текст первого типа, так как тексты второго типа состоят только из символов X

Нам целиком понадобится поле reason (кроме названия объекта и его иконки и комментария к данным), так как в нем есть данные, благодаря которым мы сможем классифицировать данную жалобу

Из поля sidebar возьмем адрес здания, его координаты, а также внутренний id в системе. В дальнейшем мы сделаем выбор в пользу того или иного способа отображения информации о доме. Помимо этого, возьмем id района, к которому относится данное здание, а также полное название муниципалитета. Возьмем также название организации, ответственной за жалобу. В некоторых записях есть координаты и адрес ближайшего здания, возьмем и их. Возьмем также информацию о пользователе, составившем обращение, вероятно, она как-то поможет, если один пользователь подавал несколько жалоб одного и того же видна

Помимо названных выше данных возьмем id из данных верхнего уровня, поскольку остальные данные служат для метрик внутри сервиса, который предоставил данные.

Получившийся массив меток будем использовать в последующих подзаданиях

1.2 Создание структуры данных для итогового набора полей

Хранить все данные будем в Pandas DataFrame. Он сочетает в себе удобство представления информации, а также удобство работы с ним

Теперь над полученным датасетом можно провести стандартные процедуры по очистке данных

1.3. Подготовка датасета

Для начала посчитаем количество значений NaN в датасете. Те столбцы, в которых этих значений слишком много, можно удалить, поскольку они хорошо не повлияют на выполнение дальнейших исследований

Как видно, в полях

Отсутствует больше половины значений. Это связано с тем, что системе иногда не удается присвоить адрес, или пользователь указал место, грубо говоря, в чистом поле.

Такие разреженные поля нам не нужны, их следует удалить.

В целом, можно удалить параметры category name и reason name, так как в датасете их численно заменяют соответствующие id, однако необходимо сохранить соответствие id и текстов. Для этих целей заведем два файла: categories.json, reasons.json, в которые сбросим эти данные.

Теперь эти данные можно безопасно удалить

Сохраним полученный датасет для дальнейшего использования

Сессия 2

2.1 Предварительная обработка данных (текста)

В данном датасете в 280 строках отсутствует текст заявления, что делает это заявление пустым. Восстановить такое заявление можно только написав с нуля, так что удалим эти заявления.

Удаление столбцов с большим количеством пустых значений было произведено в конце модуля 1.

Удалим также столбец с адресом заявления.

В данном модуле необходимо провести NLP-обработку текста. Сперва удалим лишние символы (символы перевода строк, перевода каретки) и знаки пунктуации, оставив только буквы и цифры. Попутно переведем весь текст в столбце body text в нижний регистр, чтобы одинаковые слова, написанные в разных регистрах, не считались разными. Аналогичную операцию проведем и над названием ответственного учреЖдения и муниципалитета

Есть несколько способов это сделать:

Второй метод работает более быстро, при этом выглядя более лакон и облегчает способ задания запрещенных символов.

Названия муниципалитетов и ответственных организаций можно далее не обрабатывать, поскольку у нас нет необходимости вытаскивать какой-либо смысл из их названия. Они у нас в одном стиле (в нижнем регистре, без лишних знаков), поэтому их можно закодировать. Пакет sklearn предлагает несколько видов кодировщиков:

Метод OneHotEncoder не очень хорош на наших данных, поскольку из-за его использования датасет может разрастись на несколько сотен или даже тысяч столбцов (в зависимости от разнообразия входных данных). OrdinalEncoder является расширением LabelEncoder для нескольких размерностей, однако с ним не очень удобно работать в плане вставки трансформированных данных в датафрейм. MultiLabelBinarizer генерирует матрицу следующего вида: \begin{pmatrix} 0 & 0 &1 \\ 1 & 1 &0 \\ 0 & 1 & 0 \end{pmatrix}, где в столбцах -- метки, а в строках -- собственно токены. Этот метод так же, как и OneHotEncoder создает слишком широкие, но еще и разреженные массивы, что не подходит для нашей задачи, поэтому будет проще и удобнее воспользоваться LabelEncoder.

Для поля body text приведем все слова в начальную форму. Для этого есть два основных вида обработки:

Стемминг заключается в простом отбрасывании окончаний слов, он работает более быстро. Лемматизация работает дольше, но она учитывает форму слова, то есть, например:

Очевидно, что подход стемминга нам не подходит, поэтому будем делать лемматизацию текста.

Для лемматизации можно воспользоваться двумя классическими библиотеками:

Преимущество первой библиотеки в том, что она разрабатывалась специально с учетом специфики русского языка, в то время как nltk -- более общая библиотека. Будем пользоваться первой.

Параллельно с этим надо провести токенизацию по словам. Это делается путем деления по пробелам всех слов, так как мы уже провели предобработку слов, удалив оттуда все лишние символы, оставив ровно один пробел между словами.

Далее необходимо удалить все стопслова -- слова, которые встречаются слишком часто. Они не несут смысла, и вносят много лишнего шума в датасет, что может помешать дальнейшему обучению. Хорошая коллекция стоп-слов есть в пакете nltk.

Проведя токенизацию, мы получили набор одиночных слов. К сожалению, зачастую слова по отдельности не имеют смысла. Поэтому дополним список наших слов словосочетаниями по два и три слова (биграммами и триграммами соответственно)

2.2 Поиск ключевых слов/биграмм/триграмм

Для поиска ключевых слов в датасете для каждой записи можно воспользоваться одним из нескольких основных алгоритмов:

Бинарное кодирование чем-то похоже на работу модуля MultiLabelBinarizer, показанного несколькими ячейками выше. Такой вариант нам не подойдет, потому что:

Bag of Words несколько решает эти проблемы путем снижения размерности до количества документов. Однако этот принцип, будучи принципом частотного кодирования, может в ключевые слова занести те слова, которые встречаются очень часто во всех документах.

TF-IDF -- не столько алгоритм, сколько подход к оцениванию частоты слов. Использование его позволяет снизить вес самых частоиспользуемых слов (пропорционально логарифму отношения общего числа документов к числу документов, в котором этот токен встречается), повышая при этом оценку для редкоиспользуемых слов.

Поскольку наши тексты, по сути, являются текстами общей направленности, то особого контекста здесь учитывать не нужно

Посчитаем TF-IDF оценку для текстов.

Сохраним датасет в файле preprocessed.csv

Сессия 3

3.1 Визуализация зависимостей

Прежде, чем приступать к визуализации зависимостей между объектами, сделаем визуализацию распределения переменных, представленных в датасете. Для этого воспользуемся модулем pandas_profiling, который делает очень удобный отчет по датасету и его распределению. Помимо оценки всего датасета, он строит также матрицу корреляций между объектами и графики зависимостей между графиками

Отлично. По данным анализа видно, что данные распределены довольно равномерно, что хорошо для наших целей. По данным матрицы корреляции видно, что поле category id сильно зависит от reason id, city_object id, organization_name

Для отображения распределения ключевых слов воспользуемся оценками важности на основании полученной в прошлом модуле модели TFIDF Vectorizer. Она расставила нам оценки ключевых слов на основании частоты встречаемости в тексте. Чем чаще слово встречается в различных документах, тем меньший вес он имеет. Конечно, наш документ имеет больше измерений, чем два, поэтому воспользуемся методом PCA, который сжимает данные в два измерения

Данный график представляет собой сжатую версию данных оценок ключевых слов. Важно заметить, что абсолютно все ключевые слова мы взять не смогли, потому что тогда потребовалось бы использовать слишком много памяти (слишком -- больше 300 гбайт оперативной памяти), поэтому было принято решение взять 10 % данных, что хотя бы может разместиться в памяти для построения графика

Для визуализации зависимостей построим пять графиков -- зависимостей категории обращения (category id) от:

По результатам анализа видны некоторые закономерности:

3.2 Векторизация текстов

Для векторизации текстов можно воспользоваться как минимум тремя различными алгоритмами:

Бинаризация генерирует матрицу следующего вида: \begin{pmatrix} 0 & 0 &1 \\ 1 & 0 &0 \\ 0 & 1 & 0 \end{pmatrix}, где в столбцах -- элементы словаря модели, а в строках -- собственно токены. Такой метод нам не подойдет, поскольку

Bag of Words несколько решает эти проблемы путем снижения размерности до количества документов. Однако этот принцип, будучи принципом частотного кодирования, может в ключевые слова занести те слова, которые встречаются очень часто во всех документах.

TF-IDF -- не столько алгоритм, сколько подход к оцениванию частоты слов. Использование его позволяет снизить вес самых частоиспользуемых слов (пропорционально логарифму отношения общего числа документов к числу документов, в котором этот токен встречается), повышая при этом оценку для редкоиспользуемых слов. Тем не менее, размерность такой матрицы все еще будет большой.

Модель Word2Vec в данном контексте будет лучше, потому что она позволит находить близкие по смыслу слова, что полезно, если ключевыми словами является несколько слов сразу. Эту модель также довольно удобно сохранить для дальнейшего использования.

Полученная модель "знает" о нашем тексте и связях внутри слов. Сохранив её, мы сможем использовать её в следующих модулях для выбора слов, похожих на те, что приведены в запросе пользователя

3.3. Тематическое моделирование

Для решения задачи тематического моделирования на практике почти всегда используется один из двух алгоритмов

Недостатки первого варианта -- так как число параметров зависит от числа документов, то можно случайно переобучить модель, что резко снизит её производительность на совершенно новых текстах. Помимо этого, при добавлении нового документа распределение p(тема|документ) по уже существующим формулам построить нельзя, придется перестраивать все оценки. В нашем случае, когда каждый новый документ будет поступать от множества пользователей, такой подход невозможен.

Метод латентного размещения Дирихле устраняет эти недостатки, поэтому будем пользоваться им.

Полученная модель смогла выделить некоторое количество тем. Выше они приведены как массив ключевых слов, каждый со своим весом. Видно, что много тем имеют похожие ключевые слова. Это может быть связано с тем, что пользователи писали много жалоб на одну тему с +- одним набором слов.

Интерпретируем вывод модели в более читаемый вариант.

Как интерпретировать результат визуализации?

Каждый пузырь на левом графике представляет тему. Чем больше пузырь, тем больше распространена эта тема.

Как видно, алгоритм смог выделить только одну тему, которая и получила наибольшее распространение. Это связано с тем, что пользователи сайта, вероятно, оставляют много запросов про наличие мусора или неубранность. Словом, все заявки так или иначе можно отнести к неухоженности или "убитости" близлежащих объектов.

3.4 Кластеризация текстов

Алгоритмов кластеризации существует великое множество. Какие-то из них работают лучше на одном наборе данных, какие-то на другом. Это связано с подходом, который они используют. В нашем случае данных среднее количество, число тем известно, а количество тем невелико. Поэтому, для наших данных подходят следующие алгоритмы:

Эти алгоритмы хороши тем, что они очень хорошо масштабируются, что позволит их использовать в высоконагруженных системах.

Для оценки алгоритмов кластеризации существует множество метрик, среди них:

Первая оценка -- соотношение снутрикластерной дисперсии к межкластерной. Эта оценка достаточно хороша, причем она не требует знания истинной правды.

Вторая оценка также не требует знания истинных классов, но при этом она проще третьей оценки.

Третья оценка ощутимо проще всех остальных, но её применение требует использования знания об истинных метках, а таковые у нас имеются, так что остановимся на этом.

Для снижения размерности сожмем данные с помощью PCA компрессии

Модели требуют слишком много памяти, поэтому в силу нехватки мощностей возьмем для кластеризации только 10000 первых вхождений.

Построим оценки согласно выбранной метрике

По этой метрике самым лучшим считается алгоритм спектральной кластеризации, ведь каждый кластер должен быть независим от другого, следовательно, количество информации, которую он определяет о другом кластере, должно быть как можно меньше

Подстроим гиперпараметры у Spectral clustering

К сожалению, система не обрабатывает данные достаточно быстро, поэтому полностью кластеризация не проведена.

Сессия 4

Разбиение наборов на выборки будет логичнее сделать после создания дополнительных признаков, поэтому сперва создадим дополнительные признаки.

4.2 Создание дополнительных признаков

Займемся так называемым feature engineering -- создадим несколько признаков, использование которых нам поможет в дальнейшем.

Запишем среднее значение вектора для каждого ключевого слова

Так мы создали три признака -- ключевые слова, присущие каждой категории. Векторные представления слов будем подгружать в процессе классификации.

4.1 Разбиение набора данных на обучающую и тестовую выборки

Для разбиения набора данных нет строгих ограничений. Текстовые данные -- довольно сложная система, поэтому целесообразным будет разбить данные в соотношении 80% данных -- тренировочная выборка. 20% данных -- обучающая выборка.

Такое разбиение наиболее часто встречается в реализациях алгоритмов, работающих с текстовыми данными. Более того, использование 80 процентов данных в качестве тренировочной выборки позволяет алгоритму классификации выявить если не все, то большинство закономерностей в наблюдаемых данных. Перемешивание данных позволит выбрать набор случайных причин, а распределение по категориям позволит уравновесить количество появлений данных. Сперва сбросим текстовые колонки, так как они не могут участвовать в классификации

Теперь произведем собственно разбиение

4.3 Классификация обращений граждан по категориям обращений

Для классификации данных есть множество различных алгоритмов. В нашем случае для классификации подойдут следующие алгоритмы:

Классификатор на основе поддерживающих векторов хорош тем, что достаточно хорошо подходит для работы над NLP задачами. Строя векторы, можно достаточно точно определить принадлежность той или иной точки n-мерного пространства к категории

Наивный Байесовский классификатор использует теорему Байеса для пересчета вероятностей. Этот алгоритм довольно простой в силу простоты своего математического аппарата, следовательно, он будет иметь довольно высокую скорость

В случае с random forest classifier -- это один из быстрых классификаторов, позволяющий получить довольно качественный результат. Более того, этот классификатор нормально работает на данных больших размерностей. Проведем выбор наилучших параметров для каждой из этих трех моделей.

В качестве weighted Accuracy-оценки будем использовать balanced Accuracy, так как она является частным случаем первой и применяется в случае разбалансированности классов, которая наблюдается у нас. Для F1-метрики нужно использовать параметр 'macro', потому что в таком случае метрика будет подсчитываться для каждого класса по отдельности и выводиться

Попутно, подбирая гиперпараметры, будем выводить оценки по нужным метрикам для самой лучшей из моделей.

Итак, по результатам тестирования на тестовой выборке, мы получили значения необходимых по условию метрик.

Лидером по обоим критериям является случайный лес и байесовский классификатор, в то время как linearsvc проявил себя не очень. Предполагаю, что это связано со сложностью алгоритма, из-за чего еиу не хватило количества и разнообразия данных для дообучения до нормального состояния, либо у него получилось слишком много параметров, либо комбинация этих признаков.

Сохраним параметры случайного леса для дальнейшего использования

Сессия 5

5.1 Разработка графического интерфейса программного обеспечения (либо чат-бота) для классификации типов обращений

В графическом приложении будет представлена возможность подачи заявления в компетентные органы. Благодаря использованию классификаторов, можно будет довольно точно определить необходимую категорию обращения.

Пользователю будет предложено ввести свое обращение в текстовом режиме.

Помимо этого пользователю будет необходимо выбрать объект, по отношению к которому составляется обращение (Это, например, дом, улица); Причину обращения (неухоженность парадной, например); Район, в котором наблюдается происшествие.

После этого пользователь получит результат классификации обращения с предложением выбрать, подходит эта категория ему, или нет.

Графический интерфейс будет сделан с помощью встроенной библиотеки tkinter. Эта библиотека отличается тем, что с помощью неё можно очень просто строить неплохие экранные приложения.

5.2 Реализация приложения.

Приложение реализовано в виде отдельного .py файла, который запускается с помощью ячейки ниже.

Инструкция пользователя

Инструкция пользователя приведена в файле "Инструкция пользователя приложения.docx"

  1. Введение

Приложение представляет собой программный пакет, позволяющий облегчить пользователю процесс подачи заявления в вышестоящие инстанции. Будучи построенным на алгоритмах машинного обучения, приложение может довольно быстро по заданному запросу определить необходимую категорию заявления.

  1. Начало работы

Внимание! Если уже установлены все зависимости, перечисленные в файле requirements_conda.txt или requirements.txt, этот пункт можно пропустить

Приложение представляет собой директорию app. Для запуска пользователю необходимо прежде всего иметь последнюю версию интерпретатора языка Python. После этого необходимо с помощью командной строки зайти в директорию app. Находясь там, прописать команду pip install –r requirements.txt. Эта команда установит все необходимые зависимости. Этот способ подходит не всем, по причине того, что pip может отказаться устанавливать некоторые пакеты. Гораздо проще -- создать окружение conda с файлом requirements_conda.txt и запустить его:

conda create --name <%YOUR_NAME> --file requirements_conda.txt
activate <%YOUR_NAME>

, где <%YOUR_NAME> -- ваше название без угловых скобок.

Установив все необходимые зависимости, пользователь должен ввести команду запуска python app.py

В результате появится главное окно приложения.

В следующем пункте пойдет речь о работе с ним.

  1. Подача обращения:

При подаче обращения пользователю необходимо заполнить все поля, отмеченные звездочкой, а именно: • Ввести текст обращения в поле «Впишите сюда свое обращение» • Выбрать объект, по отношению к которому составляется обращение • Выбрать причину обращения • Выбрать район обращения После выполнения этих действий необходимо нажать кнопку «отправить» Примечание: если хотя бы одно поле не заполнено, то пользователю будет выдана ошибка с предложением заполнить поля. Если поля с выбором заполнены теми данными, которые не содержатся в данном списке, ошибка тоже появится.

После нажатия кнопки «Отправить» пользователь увидит результат классификации его обращения по категории.

Если пользователь согласен с результатом классификации, то нужно нажать «Да», в противном случае «Нет».

Результатом нажатия кнопки «Да» становится информационное окно с сообщением об успешной отправке сообщения

В случае нажатия кнопки «Нет», появится информационное окно с просьбой подобрать нужное значение категории

После этого появится окно с возможностью выбрать категорию

Категорию необходимо выбрать из выпадающего списка. Иначе будет выведена ошибка. После ошибки окно выбора не закроется, но может уйти на самый задний план на экране пользователя.

После выбора нужно нажать на кнопку «Отправить», чтобы отправить сообщение в службы. Результат – окно об успешной подаче заявки.

  1. О Разработчике Разработчик приложения – Жуков Павел, участник III отраслевого чемпионата по стандартам WorldSkills «DigitalSkills-2021». В случае возникновения вопросов, обращайтесь к третьему рабочему месту.